/*		Filename: 	database_manager.cpp
 *		Author:		彭望
 *		Date:		2012-01-11
 *		Use:		定义数据库管理器的结构
 */

#include "database_manager.h"
#include <cstring>

using boost::format;

CDatabaseManager::CDatabaseManager(const string& strConfigFilename)
		:logger( Logger::getLogger() ),
		m_cardTableName("DB_TABLE_CARD")
{
	RetCode_t ret = load_config(strConfigFilename);
	if( RET_OK != ret )
	{
			return;
	}
	this->init();
}

RetCode_t CDatabaseManager::load_config(const string& strConfigFilename)
{
	using namespace libconfig;
	Config config;
	try{
			config.readFile( strConfigFilename.c_str() );
	}catch(ParseException e)
	{
			logger.error("Cannot parse config from %s!", strConfigFilename.c_str());
			goto err;
	}catch(FileIOException e)
	{
			logger.error("Cannot open config file %s!", strConfigFilename.c_str() );
			goto err;
	}

	if( !config.lookupValue("DB_NAME", m_dbName) )
	{
			logger.warn("No DB_NAME in %s, use default.", strConfigFilename.c_str());
			m_dbName = ":memory:";
	}
	return RET_OK;
err:
	return RET_INVALID_ARG;
}

RetCode_t CDatabaseManager::init()
{
		RetCode_t ret = this->open_db(m_dbName, &m_database);
		if( RET_OK != ret )
		{
				logger.error("%s : %d\topen_db error! ", __FILE__, __LINE__);
				return RET_OPEN_DB_ERROR;
		}
		ret = this->exec_sql(m_database, str(format("CREATE TABLE IF NOT EXISTS %s \n"
							"(\n"
						    "id integer PRIMARY KEY autoincrement,\n"
							"card_id integer,\n"
							"password varchar(255),\n"
							"status integer,\n"
							"start_time TIMESTAMP,\n"
							"end_time TIMESTAMP,\n"
							"balance double,\n"
							"memo varchar(255)\n"
							")") % m_cardTableName.c_str()),NULL);
		if( RET_OK != ret )
		{
				logger.error("Create Table %s error!", m_cardTableName.c_str());
		}
		return RET_OK;
}

RetCode_t CDatabaseManager::open_db(const string& dbName, sqlite3** pDB)
{
	int rc = sqlite3_open(dbName.c_str(), pDB);
	if( SQLITE_OK != rc )
	{
			logger.error("%s : %d\t error while open db, dbName : %s", __FILE__, __LINE__ ,
							dbName.c_str() );
			return RET_OPEN_DB_ERROR;
	}
	return RET_OK;
}

string CDatabaseManager::trans2SQL(OPType t, const CCardInfo& info)
{
	const char* szTableName = m_cardTableName.c_str();
	string sql;
	if( OP_ADD == t )
	{
			sql = (format("insert into "
							"%s(card_id, password, status, start_time, end_time, balance, memo)"
						   " values (%s, %s, %d, '%s', '%s', %lf, '%s')") % szTableName
							% info.get_cardId().c_str()
							% info.get_password().c_str()
							% info.get_status()
							% this->ptime2string(info.get_startTime()).c_str()
							% this->ptime2string(info.get_endTime()).c_str()
							% info.get_balance()
							% info.get_memo().c_str())
							.str().c_str();
	}else if( OP_DELETE == t )
	{/*
			sql = format("delete from %s where card_id=%s") % szTableName % info.get_cardId()
						.c_str().str();
	*/
	}else if( OP_UPDATE == t )
	{
			sql = (format("update %s set "
							" password=%s, status=%s, start_time=%s, end_time=%s, balance=%d, memo=%s"
							" where card_id=%s") % szTableName
						% info.get_password().c_str()
						% info.get_status()
						% this->ptime2string(info.get_startTime()).c_str()
						% this->ptime2string(info.get_endTime()).c_str()
						% info.get_balance()
						% info.get_memo())
						.str()
						.c_str();
	}else if( OP_QUERY == t)
	{
			sql = (format("select * from %s where card_id=%s") % szTableName % info.get_cardId()).str().c_str();
	}else{
			logger.error("%s : %d\tUnknowd optype!", __FILE__, __LINE__);
	}
	return sql;
}

int CDatabaseManager::card_callback(void *res, int argc, char **argv, char **colName)
{
		if( res == NULL )
		{
				return 0;
		}
		CCardInfo* pInfo = static_cast<CCardInfo*>(res);
		for(int i=0; i<argc; i++){
				CDatabaseManager::fill_card_info(colName[i], argv[i], *pInfo);
		}
		return 0;
}

RetCode_t CDatabaseManager::exec_sql( sqlite3* pDb, const string& sql, void* res)
{
		logger.debug("sql :\n%s", sql.c_str());
		char* errmsg;
		RetCode_t ret = RET_OK;
		if( SQLITE_OK != sqlite3_exec(pDb, sql.c_str(), CDatabaseManager::card_callback, res, &errmsg) )
		{
				logger.warn("cannot exec sql, error msg : %s", errmsg);
				ret = RET_INSERT_DB_ERROR;
		}
err:
		sqlite3_free( errmsg );
		return ret;
}

RetCode_t CDatabaseManager::add_card(const CCardInfo& info)
{
	string sql = this->trans2SQL(OP_ADD, info);
	return this->exec_sql( m_database, sql, NULL);
}

RetCode_t CDatabaseManager::delete_card(const CCardInfo& info)
{
	string sql = this->trans2SQL(OP_DELETE, info);
	return this->exec_sql( m_database, sql, NULL);
}

RetCode_t CDatabaseManager::search_card(CCardInfo& info)
{
	string sql = this->trans2SQL(OP_QUERY, info);
	if( RET_OK != this->exec_sql( m_database, sql, &info) )
	{
			logger.error("search card error!");
			return RET_QUERY_DB_ERROR;
	}
	return RET_OK;
}

RetCode_t CDatabaseManager::update_card(const CCardInfo& info)
{
		string sql = this->trans2SQL(OP_UPDATE, info);
		return this->exec_sql( m_database, sql, NULL );
}

RetCode_t CDatabaseManager::fill_card_info(const char* key, const char* value, CCardInfo& info)
{
		if( 0 == strcmp("id", key) )
		{
				info.set_id( value );
		}else if ( 0 == strcmp("card_id", key) )
		{
				info.set_cardId( value );
		}else if( 0 == strcmp("password", key) )
		{
				info.set_password( value );
		}else if( 0 == strcmp("status", key) )
		{
				int iStatus = boost::lexical_cast<int>(value);
				CCardInfo::CardStatus_t status = static_cast<CCardInfo::CardStatus_t>(iStatus);
				info.set_status( status );

		}else if( 0 == strcmp("start_time", key) )
		{
				info.set_startTime( boost::posix_time::time_from_string(value) );
		}else if( 0 == strcmp("end_time", key) )
		{
				info.set_endTime( boost::posix_time::time_from_string(value) );
		}else if( 0 == strcmp("memo", key) )
		{
				info.set_memo(value);
		}else if(0 == strcmp("balance", key) )
		{
				info.set_balance( boost::lexical_cast<double>(value));
		}else{
				Logger::getLogger().warn("Unknown key : %s, value : %s", key, value);
		}
		return RET_OK;
}

string CDatabaseManager::ptime2string(const ptime& time)
{
		/*
		boost::posix_time::time_facet* tf = new boost::posix_time::time_facet("%Y-%m-%d %H:%M:%S");
		std::stringstream ss; 
		ss.imbue(std::locale(std::cout.getloc(), tf)); 
		ss << time;
		return ss.str();
		*/
		return to_simple_string(time);
}

